package com.hexb.smh.service.impl;

import com.hexb.core.common.Page;
import com.hexb.core.exception.BusinessException;
import com.hexb.core.model.BaseErrorCodes;
import com.hexb.core.service.Pagination;
import com.hexb.core.utils.Assert;
import com.hexb.smh.entity.Brand;
import com.hexb.smh.entity.Product;
import com.hexb.smh.entity.ProductPrice;
import com.hexb.smh.entity.ProductRaw;
import com.hexb.smh.entity.enums.ImportStrategy;
import com.hexb.smh.entity.enums.ProductSortKey;
import com.hexb.smh.entity.param.product.ProductParam;
import com.hexb.smh.entity.param.product.ProductPriceParam;
import com.hexb.smh.entity.param.product.ProductQueryParam;
import com.hexb.smh.entity.view.*;
import com.hexb.smh.errors.ErrorCodes;
import com.hexb.smh.mapper.BrandMapper;
import com.hexb.smh.mapper.ProductMapper;
import com.hexb.smh.mapper.ProductPriceMapper;
import com.hexb.smh.service.IProductService;
import com.hexb.smh.utils.AccountHelper;
import com.hexb.smh.utils.POIExcelHelper;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.usermodel.CellStyle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.validation.constraints.NotNull;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.*;
import java.util.function.Consumer;

/**
 * @author : hexb
 */
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
@Slf4j
public class ProductService implements IProductService {

    @NonNull
    final private ProductMapper productMapper;

    @NonNull
    final private ProductPriceMapper productPriceMapper;

    @NonNull
    final private BrandMapper brandMapper;

    @Transactional
    @Override
    public Product save(Product product) {
        try {
            Integer uid = AccountHelper.getAccount().getId();
            product.setCreateBy(uid);
            productMapper.save(product);

            List<ProductPrice> productPrice = product.getProductPrice();
            Assert.isEmpty(productPrice, ErrorCodes.PRODUCT_PRICE_MUST_SET);
            productPrice.forEach(p -> {
                p.setProductId(product.getId());
            });
            productPriceMapper.batchSaveOrUpdate(productPrice);
        } catch (DuplicateKeyException e) {
            throw new BusinessException(ErrorCodes.DUPLICATE_PRODUCT_ITEM_NO);
        }
        return product;
    }

    @Transactional
    @Override
    public int update(Product product) {
        Assert.isNull(product, BaseErrorCodes.PARAMETER_IS_NULL);
        Assert.isNull(product.getId(), ErrorCodes.ID_IS_NULL);

        if (!StringUtils.isEmpty(product.getItemNo())) {
            ProductRaw byName = productMapper.findByItemNo(product.getItemNo());
            Assert.isTrue(byName != null && !byName.getId().equals(product.getId()), ErrorCodes.DUPLICATE_PRODUCT_ITEM_NO);
        }

        try {
            Integer uid = AccountHelper.getAccount().getId();
            product.setUpdateBy(uid);
            List<ProductPrice> pps = product.getProductPrice();
            if (CollectionUtils.isNotEmpty(pps)) {
                pps.forEach(pp -> pp.setProductId(product.getId()));
                productPriceMapper.batchSaveOrUpdate(pps);
            }
            return productMapper.updateByEntity(product);
        } catch (DuplicateKeyException e) {
            throw new BusinessException(ErrorCodes.DUPLICATE_PRODUCT_ITEM_NO);
        }
    }

    @Override
    public Page<ProductView> search(ProductQueryParam param, int pageSize, int pageNum) {
        return Pagination.pageCommonAnyType(pageNum, pageSize, () -> productMapper.search(param));
    }

    @Override
    public ProductView selectOneById(Integer id) {
        Assert.isNull(id, ErrorCodes.ID_IS_NULL);
        return productMapper.selectById(id);
    }

    @Transactional
    @Override
    public int delete(Integer id) {
        Assert.isNull(id, ErrorCodes.ID_IS_NULL);
        productPriceMapper.deleteByPriceId(id);
        return productMapper.delete(id);
    }

    @Transactional
    @Override
    public int deleteBatch(Integer[] ids) {
        Assert.isEmpty(ids, BaseErrorCodes.PARAMETER_IS_NULL);
        productPriceMapper.deleteBatchByPriceId(ids);
        return productMapper.deleteBatch(ids);
    }

    @Override
    public int toggleActivated(Integer id, Boolean activated) {
        Assert.isNull(id, ErrorCodes.ID_IS_NULL);
        Product p = new Product();
        p.setId(id);
        p.setActivated(activated);
        return productMapper.updateByEntity(p);
    }

    @Override
    public int updateActivatedBatch(Integer[] ids, Boolean activated) {
        Assert.isEmpty(ids, BaseErrorCodes.PARAMETER_IS_NULL);
        Assert.isNull(activated, BaseErrorCodes.PARAMETER_IS_NULL);
        return productMapper.updateActivatedBatch(ids, activated);
    }

    @Override
    public Page<ProductClientView> searchClient(@NotNull Integer brandId, String searchKey, int pageSize, int pageNum) {
        return Pagination.pageCommonAnyType(pageNum, pageSize, () -> productMapper.searchClient(searchKey, brandId));
    }

    @Override
    public List<ProductClientView> searchClient(@NotNull Integer brandId, String searchKey) {
        return productMapper.searchClient(searchKey, brandId);
    }

    @Override
    public List<ProductView> search(String searchKey) {
        ProductQueryParam p = new ProductQueryParam();
        p.setSearchKey(searchKey);
        p.setSortKey(ProductSortKey.briefCode);
        p.setActivated(true);
        return productMapper.search(p);
    }


    @Override
    public ProductRaw findOneByMap(Map<?, ?> param) {
        return productMapper.findOneByMap(param);
    }

    //导入
    @Override
    public ImportResult<Product> importProduct(MultipartFile file, ImportStrategy importStrategy) {
        ImportResult<Product> res = new ImportResult<>();
        List<Brand> brands = brandMapper.list();
        if (null == file) return res;
        String fileName = file.getOriginalFilename();
        try (InputStream is = file.getInputStream()) {
            List<Product> products = POIExcelHelper.read(is, r -> {
                ProductParam.ProductParamInsert pp = new ProductParam.ProductParamInsert();
                int i = 0;
                pp.setName(POIExcelHelper.getCellValue(r.getCell(i++)));
                pp.setItemNo(POIExcelHelper.getCellValue(r.getCell(i++)));
                pp.setBriefCode(POIExcelHelper.getCellValue(r.getCell(i++)));
                pp.setDescription(POIExcelHelper.getCellValue(r.getCell(i++)));
                pp.setRemarks(POIExcelHelper.getCellValue(r.getCell(i++)));
                boolean activated = "是".equals(POIExcelHelper.getCellValue(r.getCell(i++)));
                pp.setCategory(POIExcelHelper.getCellValue(r.getCell(i++)));

                List<ProductPriceParam> list = new ArrayList<>();
                int p = brands.size();
                for (int n = 0; n < p; n++) {
                    ProductPriceParam pr = new ProductPriceParam();
                    pr.setFixedCostPrice(POIExcelHelper.getCellValue(r.getCell(i++)));
                    pr.setVolatileCostPrice(POIExcelHelper.getCellValue(r.getCell(i++)));
                    pr.setOtherCostPrice(POIExcelHelper.getCellValue(r.getCell(i++)));
                    pr.setMarketPrice(POIExcelHelper.getCellValue(r.getCell(i++)));
                    pr.setBrandId(brands.get(n).getId());
                    if (pr.getFixedCostPrice() == null || pr.getVolatileCostPrice() == null || pr.getOtherCostPrice() == null) {
                        continue;
                    }
                    list.add(pr);
                }
                pp.setPrices(list);

                Product product = pp.toProduct();
                product.setActivated(activated);
                product.setCreateBy(AccountHelper.getAccount().getId());
                return product;
            }, fileName);

            if (CollectionUtils.isNotEmpty(products)) {
                res.setTotal(products.size());
                switch (importStrategy) {
                    case replaceAll: {
                        productMapper.clear();
                        saveAndRecord(products, productMapper::save, res);
                        break;
                    }
                    case overwrite: {
                        saveAndRecord(products, (product) -> {
                            productMapper.saveOrUpdate(product);
                            List<ProductPrice> productPrice = product.getProductPrice();
                            if (CollectionUtils.isNotEmpty(productPrice)) {
                                productPrice.forEach(p -> {
                                    p.setProductId(product.getId());
                                });
                                productPriceMapper.batchSaveOrUpdate(productPrice);
                            }
                        }, res);
                        break;
                    }
                    default: {
                        saveAndRecord(products, productMapper::save, res);
                    }
                }
            }
        } catch (IOException e) {
            res.setMessage("读取文件出错:" + e.getMessage());
            log.error("读取产品文件出错", e);
        }
        return res;
    }

    protected void saveAndRecord(List<Product> products, Consumer<Product> saver, ImportResult<Product> res) {
        products.forEach(p -> {
            try {
                saver.accept(p);
            } catch (DuplicateKeyException e) {
                res.addFailed(p, "项目编号重复");
            } catch (Exception e) {
                log.error("保存项目发生错误", e);
                res.addFailed(p, "发生错误 : " + e.getClass().getName());
            }
        });
    }

    private static final String[] headerTexts = new String[]{
            " 项目名称 ",
            " 项目编码 ",
            " 拼音简码 ",
            "产品描述(业务员可见)",
            "产品备注(仅管理员可见)",
            " 是否上架 ",
            " 分类 "

    };

    private static final String[] priceHeaderTexts = new String[]{
            "固定成本价(元)",
            "变动成本价(元)",
            "其它成本价(元)",
            "市场价(元)",
    };

    @Override
    public void export(OutputStream os) {
        List<Brand> brands = brandMapper.list();
        HSSFWorkbook workbook = new HSSFWorkbook();
        HSSFSheet sheet = workbook.createSheet();

        HSSFCellStyle headerStyle = POIExcelHelper.headerStyle(workbook);
        headerStyle.setFillPattern(CellStyle.SOLID_FOREGROUND);
        headerStyle.setFillForegroundColor(HSSFColor.LIME.index);
        HSSFCellStyle style = POIExcelHelper.style(workbook);
        //添加header
        final HSSFRow headerRow = sheet.createRow(0);
        for (int i = 0; i < headerTexts.length; i++) {
            HSSFCell cell = headerRow.createCell(i);
            String text = headerTexts[i];
            cell.setCellStyle(headerStyle);
            cell.setCellValue(text);
            sheet.setColumnWidth(i, text.length() * 2 * 256);
        }

        for (int x = 0; x < brands.size(); x++) {
            Brand b = brands.get(x);
            for (int i = 0; i < priceHeaderTexts.length; i++) {
                String text = b.getName() + priceHeaderTexts[i];
                //x:品牌索引
                int ci = x * 4 + i + headerTexts.length;
                HSSFCell cell = headerRow.createCell(ci);
                cell.setCellStyle(headerStyle);
                cell.setCellValue(text);
                sheet.setColumnWidth(ci, text.length() * 2 * 256);
            }
        }


        List<ProductView> list = productMapper.search(new ProductQueryParam());
        if (CollectionUtils.isNotEmpty(list)) {
            int size = list.size();
            for (int i = 0; i < size; i++) {
                final HSSFRow row = sheet.createRow(i + 1);
                ProductView p = list.get(i);
                int c = 0;
                writeCellValue(row, c++, style, p.getName());
                writeCellValue(row, c++, style, p.getItemNo());
                writeCellValue(row, c++, style, p.getBriefCode());
                writeCellValue(row, c++, style, p.getDescription());
                writeCellValue(row, c++, style, p.getRemarks());
                writeCellValue(row, c++, style, p.getActivated() ? "是" : "否");
                writeCellValue(row, c++, style, p.getCategory());

                List<ProductPriceView> prices = p.getPrices();
                for (ProductPriceView ppv : prices) {
                    writeCellValue(row, c++, style, ppv.getFixedCostPrice());
                    writeCellValue(row, c++, style, ppv.getVolatileCostPrice());
                    writeCellValue(row, c++, style, ppv.getOtherCostPrice());
                    writeCellValue(row, c++, style, ppv.getMarketPrice());
                }
            }
        }
        try {
            workbook.write(os);
        } catch (IOException e) {
            log.error("项目内容写入excel文件出错", e);
        }
    }

    private void writeCellValue(HSSFRow row, int index, HSSFCellStyle style, String value) {
        HSSFCell cell = row.createCell(index);
        cell.setCellStyle(style);
        cell.setCellValue(value);
    }

    @Override
    public List<ProductBrandView> findWithBrandId(Integer brandId, Integer[] ids, Boolean activated, String searchKey) {
        return productMapper.findWithBrandId(brandId, ids, activated, searchKey);
    }

    @Override
    public List<Product> listByIds(Integer[] ids) {
        if (null == ids || ids.length < 1) {
            return Collections.emptyList();
        }
        return productMapper.listByIds(ids);
    }
}
